package com.yuebao.service.impl;


import cn.hutool.core.date.StopWatch;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yuebao.api.FundProductionService;
import com.yuebao.api.TransactionService;
import com.yuebao.enumeration.ResponseCodeEnum;
import com.yuebao.exception.CustomerException;
import com.yuebao.feign.FundServiceCloud;
import com.yuebao.feign.PaymentCloud;
import com.yuebao.feign.UserAccountCloud;
import com.yuebao.mapper.TransactionMapper;
import com.yuebao.model.pojo.FundProduction;
import com.yuebao.model.pojo.Transaction;
import com.yuebao.model.pojo.UserAccount;
import com.yuebao.request.RequestParameter;
import com.yuebao.result.BaseResponse;
import com.yuebao.result.EntityResponse;
import com.yuebao.utils.CacheClient;
import com.yuebao.utils.IDWorker;
import com.yuebao.utils.JsonUtils;
import com.yuebao.utils.RedisConstants;
import com.yuebao.utils.ResponseUtils;
import lombok.extern.slf4j.Slf4j;
//import org.apache.dubbo.config.annotation.DubboReference;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import javax.validation.constraints.NotNull;
import java.math.BigDecimal;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
* @author homson_leo
* @description 针对表【transaction(流水表)】的数据库操作Service实现
* @createDate 2023-07-02 11:31:48
*/
@Service
@Slf4j
public class TransactionServiceImpl extends ServiceImpl<TransactionMapper, Transaction>
    implements TransactionService {

    @Resource
    private IDWorker idWorker;

    @Resource
    private UserAccountCloud userAccountCloud;
    
    @Resource
    private FundServiceCloud fundServiceCloud;

    @Resource
    private StringRedisTemplate stringRedisTemplate;
    
    @Autowired
    CacheClient cacheClient;
    
    @Resource
    private PaymentCloud paymentCloud;
//    @DubboReference
//    private FundProductionService fundProductionService;

    @Resource
    private RedissonClient redissonClient;

    @Override
    public String buyFund(Transaction preOrder) throws Exception {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();

        try{
            //订单校验
            checkOrder(preOrder);
            //扣Cache库存
            handleOrder(preOrder);
            System.out.println("Cache扣库存成功");
            //赋予UUID 和订单状态 订单存数据库
            saveTransHistory(preOrder,"待支付");
            //异步支付
            String form = handlePayment(preOrder);
//            stopWatch.stop();
//            System.out.println(stopWatch.getTotalTimeMillis());
            
            return form;

        }catch (Exception e){
             this.saveTransHistory(preOrder,"下单失败");
             log.error(String.valueOf(e.getMessage()));
           
             log.error(String.valueOf(e.getStackTrace()));
            //发送到取消队列 进行回滚

            return "";

        }
    }
    
    private void saveTransHistory(@NotNull Transaction preOrder,@NotNull String state) {
        preOrder.setOrderNo(idWorker.nextId());
        preOrder.setOrderState(state);
        this.save(preOrder);
        //再缓存一份
        cacheClient.set(CacheClient.Order_History+preOrder.getOrderNo(),preOrder.getOrderState(),Long.valueOf(1000),TimeUnit.SECONDS);
    }
    
    
    private String handlePayment(Transaction preOrder) throws CustomerException {
        //给支付宝发送
        EntityResponse<String> paymentResponse = paymentCloud.handlePayment(new RequestParameter<>(preOrder));
        if(paymentResponse.isResponseRight()){
            return paymentResponse.getData();
        }
        throw new CustomerException("支付失败");
    }

    private void handleOrder(Transaction preOrder) throws Exception {
        Long fundId = preOrder.getFundId();
        BigDecimal share = preOrder.getShare();

        //加锁扣库存
        if(!fundStockTryLock(fundId)){
            throw new Exception("系统繁忙");
        }
        try{
            //扣cache库存
            BigDecimal stock = BigDecimal.valueOf(Long.parseLong(stringRedisTemplate.opsForValue().get("fund_stock:" + fundId)));
            //预判断库存
            if((stock.subtract(share)).compareTo(BigDecimal.ZERO) < 1) {
                throw new Exception("库存不足");
            }
            stringRedisTemplate.opsForValue().set(RedisConstants.FUND_STOCK + fundId, String.valueOf(stock.subtract(share)));
        }catch (Exception e){
            throw new Exception(e.getMessage());
        }finally {
            //解锁
            fundStockUnLock(fundId);
        }



    }

    private void checkOrder(Transaction preOrder) throws Exception {
        //检查 用户是否存在 状态
    
        // UserAccount userAccount = userAccountService.query().eq("user_id",preOrder.getUserId()).one();
        EntityResponse<UserAccount> userAccountDTO = userAccountCloud.getUserAccountById(preOrder.getUserId());
        UserAccount userAccount = ResponseUtils.getResponseData(userAccountDTO);
        log.info("检查 用户是否存在 信息==》{}", JsonUtils.writeValueAsString(userAccount));
        if (userAccount==null){
            throw new Exception("用户账户不存在");
        }
 
        //检查 基金金额 单价 份额是否合法 lua脚本
        Long fundId = preOrder.getFundId();
        if(Objects.isNull(fundId))
            throw new Exception("基金id为空");
        //String fund = redisTemplate.opsForValue().get("fund_production:" + fundId);
        FundProduction fundProduction = cacheClient.queryWithPassThrough("fund_production:", fundId, FundProduction.class, fundServiceCloud::getFundProductionById, RedisConstants.CACHE_SHOP_TTL, TimeUnit.MINUTES);
        
        
        if (Objects.isNull(fundProduction))
            throw new Exception("基金不存在");
        //简单查询库存是否充足
        BigDecimal stock = BigDecimal.valueOf(Long.parseLong(stringRedisTemplate.opsForValue().get("fund_stock:" + fundId)));
        BigDecimal share = preOrder.getShare();
        BigDecimal sharePrice = fundProduction.getSharePrice();
        BigDecimal tradeMoney = preOrder.getTradeMoney();
        //预判断库存
        if((stock.subtract(share)).compareTo(BigDecimal.ZERO) < 1) {
            throw new Exception("库存不足");
        }
        //核算金额
        if (!(share.multiply(sharePrice)).equals(tradeMoney)){
            throw new Exception("金额核算失败");
        }
        
    }
    private boolean fundStockTryLock(Long fundId) throws CustomerException {
        RLock lock = redissonClient.getLock(RedisConstants.FUND_STOCK + fundId);
        try {
            return  lock.tryLock(5,180,TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            e.printStackTrace();
            throw new CustomerException("分布式锁错误："+e.getMessage());
        }
    }
    
    private boolean fundStockUnLock(Long fundId) throws CustomerException {
        RLock lock = redissonClient.getLock(RedisConstants.FUND_STOCK + fundId);
        lock.unlock();
        return true;
    }
    
    
    
}




